using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Drawing;
using System.Data;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Windows.Forms;
using Common.Utilities;
using Common.Utilities.DLR;
using Common.Utilities.Documents;
using Northwoods.Go;
using Workflows.Components.Rules;
using Rule=Workflows.Components.Rules.Rule;

namespace Rules.Diagram
{
    /// <summary>
    /// user control that holds rule design document
    /// </summary>
    public partial class RuleDocControl : UserControl
    {
        #region fields
        private RuleView _View = null;
        private Rule _Rule;
        
        #endregion

        #region events
        public event DocumentOpened OnDocumentOpened;
        public event DocumentClosed OnDocumentClosed;
        public event DocumentSaved OnDocumentSaved;
        public event ObjectSelected OnObjectSelected;
        public event UpdateCopyCommand UpdateCopyCommandEvent;
        public event UpdateCutCommand UpdateCutCommandEvent;
        public event UpdatePasteCommand UpdatePasteCommandEvent;
        public event UpdateRedoCommand UpdateRedoCommandEvent;
        public event UpdateUndoCommand UpdateUndoCommandEvent;
        #endregion

        #region init
        /// <summary>
        /// 
        /// </summary>
        public RuleDocControl()
        {
            InitializeComponent();

            this._Rule = new Rule();
            this.InitRuleView();
        }

        private void InitRuleView()
        {
            this.Controls.Clear();
            this._View = new RuleView();
            this.Controls.Add(this._View);
            this._View.Dock = DockStyle.Fill;
            this._View.AllowCopy = false;
            this._View.AllowDragOut = false;
            this._View.AllowLink = false;
            this._View.AllowMove = true;
            this._View.AllowResize = true;
            //this._View.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
            //  | System.Windows.Forms.AnchorStyles.Left)
            //  | System.Windows.Forms.AnchorStyles.Right)));
            this._View.ArrowMoveLarge = 5F;
            this._View.ArrowMoveSmall = 1F;
            this._View.BackColor = System.Drawing.Color.White;
            this._View.BoundingHandlePenWidth = 3F;
            this._View.DisableKeys = Northwoods.Go.GoViewDisableKeys.None;
            this._View.GridCellSizeHeight = 5F;
            this._View.GridCellSizeWidth = 5F;
            this._View.GridSnapDrag = Northwoods.Go.GoViewSnapStyle.Jump;
            this._View.Highlit = null;
            //this._View.Location = new System.Drawing.Point(168, 8);
            this._View.Name = "RuleView";
            this._View.NoFocusSelectionColor = System.Drawing.Color.Blue;
            this._View.PrimarySelectionColor = System.Drawing.Color.Blue;
            this._View.SecondarySelectionColor = System.Drawing.Color.MediumBlue;
            //this._View.Size = new System.Drawing.Size(680, 656);
            this._View.TabIndex = 0;
            this._View.Text = this._Rule.RuleName;
            this._View.Document.UserObject = this._Rule;
            // this._View.AllowMove = true;
            
            // selection event handler
            this._View.ObjectGotSelection += new Northwoods.Go.GoSelectionEventHandler(this._View_ObjectGotSelection);
            this._View.ObjectLostSelection += new Northwoods.Go.GoSelectionEventHandler(this._View_ObjectLostSelection);
            this._View.ObjectEdited += new Northwoods.Go.GoSelectionEventHandler(this._View_ObjectEdited);
            this._View.BackgroundSingleClicked += new GoInputEventHandler(_View_BackgroundSingleClicked);
            

            // drag-drop handled in view 
            this._View.DragDrop += new DragEventHandler(_View_DragDrop);

            // double click 
            this._View.ObjectDoubleClicked += new GoObjectEventHandler(_View_ObjectDoubleClicked);
            this._View.BackgroundDoubleClicked += new GoInputEventHandler(_View_BackgroundDoubleClicked);
            //RuleNode start = new RuleNode(RuleNodeType.Start);
            //start.Position = new PointF(100, 100);
            //this._View.Document.Add(start);

            //RuleNode end = new RuleNode(RuleNodeType.End);
            //end.Position = new PointF(100, 400);
            //this._View.Document.Add(end);
        }

        #region drag-drop
        void _View_DragDrop(object sender, DragEventArgs e)
        {
            if (this.ParentForm != null && this.ParentForm.GetType().GetInterface(typeof(IMainForm).Name) != null)
            {
                IMainForm mainForm = (IMainForm) this.ParentForm;
                if (mainForm.DraggedObject.GetType().IsSubclassOf(typeof(Rule)))
                {
                    Rule rule = (Rule) mainForm.DraggedObject;
                    if (rule != null)
                    {
                        RuleNode ruleNode = this.CreateRuleNode(rule);
                        Point pos = new Point(e.X, e.Y);
                        Point viewPoint = this._View.PointToClient(pos);
                        ruleNode.Position = viewPoint;
                        try
                        {
                            this._View.Document.Add(ruleNode);
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                        }
                        mainForm.DraggedObject = null;
                    }
                }
            }
        }

        private RuleNode CreateRuleNode(Rule rule)
        {
            RuleNode ruleNode = new RuleNode(RuleNodeType.Conditional);
            ruleNode.Text = rule.RuleName;
            return ruleNode;
        }
        #endregion

        #region double click
        void _View_ObjectDoubleClicked(object sender, GoObjectEventArgs e)
        {
            RuleNode ruleNode = null;

            if (e.GoObject is RuleNode)
            {
                ruleNode = (RuleNode) e.GoObject;
            }
            else
            {
                GoObject current = e.GoObject;
                while(current.Parent !=null)
                {
                    current = current.Parent;
                    if (current is RuleNode)
                    {
                        ruleNode = (RuleNode)current;
                        break;
                    }
                }
            }

            if (ruleNode != null)
            {
                RuleNodeDataBase ruleNodeData = (RuleNodeDataBase) ruleNode.UserObject;
                switch (ruleNode.Kind)
                {
                    case RuleNodeType.Conditional:
                        if(ruleNode.UserObject.GetType()== typeof(DeclarativeRuleCondition))
                        {
                            RuleConditionEditor editor1 = new RuleConditionEditor();
                            DeclarativeRuleCondition condition = (DeclarativeRuleCondition) ruleNodeData;
                            editor1.Show(this.ParentForm);
                            editor1.InitRuleCondition(ruleNode, this._Rule, condition);
                            editor1.OnRuleConditionUpdated += new RuleDataUpdated(editor1_OnRuleConditionUpdated);
                        }
                        else if (ruleNode.UserObject.GetType() == typeof(CodeRuleCondition))
                        {
                            CodeRuleConditionEditor editor1b=new CodeRuleConditionEditor();
                            CodeRuleCondition conditionb = (CodeRuleCondition) ruleNode.UserObject;
                            editor1b.Show(this.ParentForm);
                            editor1b.InitRuleCondition(ruleNode,this._Rule,conditionb);
                            editor1b.OnRuleConditionUpdated +=new RuleDataUpdated(editor1_OnRuleConditionUpdated);
                        }
                        else if (ruleNode.UserObject.GetType() == typeof(ClassRuleCondition))
                        {
                            ClassRuleConditionEditor editor1c=new ClassRuleConditionEditor();
                            ClassRuleCondition condition1c = (ClassRuleCondition) ruleNode.UserObject;
                            editor1c.Show(this.ParentForm);
                            editor1c.InitRuleCondition(ruleNode,this._Rule,condition1c);
                            editor1c.OnRuleConditionUpdated +=new RuleDataUpdated(editor1_OnRuleConditionUpdated);
                        }
                        break;
                    case RuleNodeType.Action:
                        RuleActionEditor editor2 = new RuleActionEditor();
                        editor2.Show(this.ParentForm);
                        break;
                    case RuleNodeType.Effect:
                        RuleEffectEditor editor3 = new RuleEffectEditor();
                        editor3.Show(this.ParentForm);
                        break;
                    case RuleNodeType.Output:
                        RuleOutputEditor editor4 = new RuleOutputEditor();
                        editor4.Show(this.ParentForm);
                        break;
                    default:
                        break;
                }
            }
        }

        void editor1_OnRuleConditionUpdated(object ownerRuleNode, Rule rule, RuleNodeDataBase ruleData)
        {
            RuleNode ruleNode = (RuleNode) ownerRuleNode;
            ruleNode.UserObject = ruleData;
        }

        void _View_BackgroundDoubleClicked(object sender, GoInputEventArgs e)
        {
            RuleEditor editor=new RuleEditor();
            editor.Show(this.ParentForm);
            editor.Rule = this._Rule;
            editor.OnRuleUpdated += new RuleUpdated(editor_OnRuleUpdated);
        }

        void editor_OnRuleUpdated(Rule rule)
        {
            this._Rule = rule;
        }
        #endregion

        #endregion

        #region properties
        /// <summary>
        /// 
        /// </summary>
        public RuleDoc Document
        {
            get
            {
                return this._View.Doc;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsDirty
        {
            get
            {
                return this._View.Doc.IsDirty;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public RuleView DesignView
        {
            get
            {
                return this._View;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Rule Rule
        {
            get
            {
                return this._Rule;
            }
        }
        #endregion

        #region selection
        void _View_BackgroundSingleClicked(object sender, GoInputEventArgs e)
        {
            if (this.OnObjectSelected != null)
                this.OnObjectSelected(this, new SelectedObjectEventArgs(this._View.Doc.UserObject));
        }

        private void _View_ObjectEdited(object sender, GoSelectionEventArgs e)
        {
            UpdatePropertyGrid();
        }

        private void _View_ObjectLostSelection(object sender, GoSelectionEventArgs e)
        {
            UpdatePropertyGrid();
        }

        private void _View_ObjectGotSelection(object sender, GoSelectionEventArgs e)
        {
            UpdatePropertyGrid();
        }

        /// <summary>
        /// display selected obj in parent form property grid
        /// </summary>
        public void UpdatePropertyGrid()
        {
            object selectedObj = null;
            if (!this._View.Selection.IsEmpty)
            {
                IGoNode node = this._View.Selection.Primary as IGoNode;
                if (node != null && node.UserObject != null)
                {
                    selectedObj = node.UserObject;
                }
            }
            if (this.OnObjectSelected != null)
                this.OnObjectSelected(this, new SelectedObjectEventArgs(selectedObj)); 
        }
        #endregion

        #region save
        private string SaveBinaryFile(string ruleName)
        {
            string ruleFolderPath = FileUtil.GetWindowsAppFolderPath("Rules");
            string filePath = Path.Combine(ruleFolderPath, ruleName + ".rule");
            int index = 0;
            while(File.Exists(filePath))
            {
                index++;
                filePath = Path.Combine(ruleFolderPath, ruleName + "_" + index.ToString() + ".rule");
            }
            FileStream fs = File.OpenWrite(filePath);
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(fs, this._View.Document);
            fs.Flush();
            fs.Close();
            formatter = null;
            return filePath;
        }

        /// <summary>
        /// 
        /// </summary>
        public Rule Save()
        {
            List<RuleNode> ruleNodes = this._View.Doc.Save();
            this._View.AllowMove = true;
            List<RuleNodeDataBase> ruleNodeDataColl =new List<RuleNodeDataBase>();
            if(ruleNodes !=null && ruleNodes.Count>0)
            {
                foreach(RuleNode ruleNode in ruleNodes)
                {
                    RuleNodeDataBase ruleNodeData = (RuleNodeDataBase) ruleNode.UserObject;
                    ruleNodeDataColl.Add(ruleNodeData);
                }
            }
            if (this._View.Doc.StartNode != null && this._View.Doc.StartNode.UserObject != null)
                this._Rule.Start = (RuleInitializer) this._View.Doc.StartNode.UserObject;
            else
                this._Rule.Start = null;

            if(this._View.Doc.EndNode !=null && this._View.Doc.EndNode.UserObject !=null)
                this._Rule.Finish = (RuleFinalizer) this._View.Doc.EndNode.UserObject;
            else
                this._Rule.Finish = null;
            
            if (ruleNodeDataColl.Count > 0)
                this._Rule.RuleNodes = ruleNodeDataColl.ToArray();
            else
                this._Rule.RuleNodes = null;


            this._Rule.RuleDesignFilePath = this.SaveBinaryFile(this._Rule.RuleName);
            ORMapping<Rule> orm = new ORMapping<Rule>();
            // orm.SaveToDB(ref this._Rule, ConfigurationManager.ConnectionStrings["Workflow"].ConnectionString);
            orm.Update(ConfigurationManager.ConnectionStrings["Workflow"].ConnectionString, ref this._Rule);
            return this._Rule;
        }
        #endregion

        #region open
        /// <summary>
        /// 
        /// </summary>
        /// <param name="filePath"></param>
        public void OpenFile(string filePath)
        {
            FileStream fs = File.OpenRead(filePath);
            BinaryFormatter formatter = new BinaryFormatter();
            GoDocument docObj = formatter.Deserialize(fs) as GoDocument;
            if (docObj != null)
            {
                this._View.Document = docObj;
                if(docObj.UserObject !=null && docObj.UserObject.GetType()==typeof(Rule))
                {
                    this._Rule = (Rule) docObj.UserObject;
                }
                this._Rule.RuleDesignFilePath = filePath;
                this.UpdateRuleNodeImages();
            }
            else
            {
                MessageBox.Show("Unable to open rule design doc");
            }
            fs.Close();

        }

        private void UpdateRuleNodeImages()
        {
            List<RuleNode> ruleNodes = this._View.Doc.Save();
            if (ruleNodes != null && ruleNodes.Count > 0)
            {
                foreach (RuleNode ruleNode in ruleNodes)
                {
                    if (ruleNode.UserObject != null && ruleNode.UserObject.GetType().IsSubclassOf(typeof(RuleNodeDataBase)))
                    {
                        RuleNodeDataBase ruleNodeData = (RuleNodeDataBase)ruleNode.UserObject;
                        if (ruleNodeData.RuleNodeType != RuleNodeType.Start && ruleNodeData.RuleNodeType != RuleNodeType.End)
                            ruleNode.Image.Image = ruleNodeData.Img;
                    }
                }
            }
        }

        #endregion
    }
}
